import numpy as np
from pickle import load


def sigmoid(x):
    return np.where(x > 0, 1 / (1 + np.exp(-x)), 1 - 1 / (1 + np.exp(x)))


def logistic(p, y):
    return -np.log(sigmoid(-y * p))


def hinge(p, y):
    return np.maximum(0., 1 - p * y)


def deriv_logistic(p, y):
    return -y * sigmoid(-y * p)


def deriv_hinge(p, y):
    return -y * int((1 - p * y) >= 0)


def load_pickle(filename):
    with open(filename, "rb") as f:
        X, y = load(f)
    return X, y


class EvolvingDataset:
    '''
                ┌───────────────────┬──────────────────┐
                │   Feature Set 1   |  Feature Set 2   |
                └───────────────────┴──────────────────┘ 
   -------------┌───────────────────┐
   |            │                   │                  
   |            │                   │                  
   |            │                   │                  
 data_T1        │                   │                  
   |            │                   │                  
   |            │                   │                  
   | T_split-B--├───────────────────┼──────────────────┐-----------
   |            │                   │                  │          |
   |            │                   │                  │          |
   ---T_split---└───────────────────┼──────────────────┤          |
                                    │                  │          |
                                    │                  │       data_T2
                                    │                  │          |
                                    │                  │          |
                                    │                  │          |
                                    │                  │          |
                                    └──────────────────┘-----------
    '''

    def __init__(
        self,
        X,
        y,
        T_split: int = None,
        D_split: int = None,
        B: int = 20,
    ) -> None:
        self.X = X
        self.y = y
        self.B = B
        self.T_split = T_split if T_split else len(X) // 2 + B // 2
        self.D_split = D_split if D_split else X.shape[1] // 2

    def get_data(self):
        return (
            self.X[:self.T_split, :self.D_split],
            self.y[:self.T_split],
            self.X[self.T_split - self.B:, self.D_split:],
            self.y[self.T_split - self.B:],
        )
